package com.qire.antsbinder;

import com.android.dx.stock.ProxyBuilder;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyFactory {

    public static final String CGLIB_LIB = "CGLIB_LIB";
    public static final String JDK_LIB = "JDK_LIB";

    private static String proxyLibrary = JDK_LIB;//目前先不开放CGLIB

    public static boolean isUseCglib() {
        try {
            Class<?> cglibCls = Class.forName("leo.android.cglib.proxy.Enhancer");
            if(cglibCls == null || proxyLibrary == JDK_LIB){
                return false;
            }else{
                return true;
            }
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    private final ProxyHandler proxyHandler;

    public DynamicProxyFactory(ProxyHandler proxyHandler) {
        this.proxyHandler = proxyHandler;
    }

    public <T> T createProxy(Class<T> superClass) {
        T proxySubject = null;
        if (isUseCglib()) {
            System.out.println("CGLIB proxy");
            proxySubject = new CglibDynamicProxy().create(superClass);
        } else {
            System.out.println("JDK proxy");
            proxySubject = new JdkDynamicProxy().create(superClass);
        }
        return proxySubject;
    }

    private class JdkDynamicProxy {
        public <T> T create(final Class<T> superClass){
            return (T) Proxy.newProxyInstance(
                    superClass.getClassLoader(),
                    new Class<?>[]{superClass},
                    new ProxyHandler(superClass));
        }

        private class ProxyHandler implements InvocationHandler {

            private final Platform platform = Platform.get();
            private final Object[] emptyArgs = new Object[0];
            private final Class<?> superClass;

            private ProxyHandler(Class<?> superClass) {
                this.superClass = superClass;
            }

            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//                if(method.getDeclaringClass() == Object.class) {
//                    return method.invoke(this,args);
//                }
                if (Object.class.equals(method.getDeclaringClass())) {
                    return method.invoke(proxyHandler,args);
                }
                if (platform.isDefaultMethod(method)){
                    args = args != null ? args : emptyArgs;
                    return platform.invokeDefaultMethod(method, superClass, proxy, args);
                }
                if (!proxyHandler.parse(proxy,method,args)) {
                    Method proxyHandlerMethod = proxyHandler.getClass().getDeclaredMethod(method.getName(),method.getParameterTypes());
                    return proxyHandlerMethod.invoke(proxyHandler,args);
                }

                Object result;

                // 在转调具体目标对象之前，可以执行一些功能处理
                proxyHandler.doBefore(proxy,method,args);
                // 转调具体目标对象的方法
                result = proxyHandler.doHandle(proxy,method,args);
                // 在转调具体目标对象之后，可以执行一些功能处理
                proxyHandler.doAfter(proxy,method,args);

                return result;
            }
        }

    }

    private class CglibDynamicProxy {
        public <T> T create(final Class<T> superClass) {
            Class<?> delegateClass = superClass.isInterface() ? Object.class : superClass;
            // superClass.getInterfaces() 字节码构建时会遍历委托对象的接口方法，所以如果是借口给null
            Class<?> interfaces = superClass.isInterface() ? superClass : null;

            ProxyBuilder proxyBuilder = ProxyBuilder.forClass(delegateClass)
                    .implementing(interfaces)
//                    .dexCache(Context.getDir("dx", Context.MODE_PRIVATE))
                    .markTrusted()
//                    .parentClassLoader(delegateClass.getClassLoader())
//                    .withSharedClassLoader()
                    .handler(cglibInterceptor);

            // 暂不需要支持到，默认情况下都应该是提供了空构造。
//            if(superClass.isMemberClass() && !Modifier.isStatic(superClass.getModifiers())) {
//                // 非静态内部类，需要提供外部类作为构造函数.
//                proxyBuilder.constructorArgTypes();
//                proxyBuilder.constructorArgValues();
//            }

            try {
                return (T) proxyBuilder.build();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        private final InvocationHandler cglibInterceptor = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                if(method.getDeclaringClass() == Object.class) {
                    return method.invoke(proxy, args);
                }
                if(!proxyHandler.parse(proxy,method, args)) {
                    return method.invoke(proxyHandler, args);
                }

                Object result;

                // 在转调具体目标对象之前，可以执行一些功能处理
                proxyHandler.doBefore(proxy, method, args);
                // 转调具体目标对象的方法
                result = proxyHandler.doHandle(proxy, method, args);
                // 在转调具体目标对象之后，可以执行一些功能处理
                proxyHandler.doAfter(proxy, method, args);

                return result;
            }
        };

    }

    /**
     * 代理处理器
     */
    public interface ProxyHandler {
        /**
         * 解析方法，判断是否需要代理处理。需要则会调用{@link ProxyHandler#doHandle doHandle}
         * @param proxy     代理对象
         * @param method    代理方法
         * @param args      方法参数
         * @return  如需要代理处理返回true，否则返回false;
         */
        boolean parse(Object proxy, Method method, Object[] args);

        /**
         * 代理处理，该方法会替换具体代理行为，例如用新的行为替换，亦或是编织抽象行为的具体方法，如果不需要替换，则应该使用 method.invoke(proxied, args);
         * @param proxy     代理对象
         * @param method    代理方法
         * @param args      方法参数
         * @param <T>       代理方法的返回值类型
         * @return          返回代理方法的返回值
         */
        <T> T doHandle(Object proxy, Method method, Object[] args);

        /**
         * 代理处理之前做的事
         * @param proxy     代理对象
         * @param method    代理方法
         * @param args      方法参数
         */
        void doBefore(Object proxy, Method method, Object[] args);

        /**
         * 代理处理之后做的事
         * @param proxy     代理对象
         * @param method    代理方法
         * @param args      方法参数
         */
        void doAfter(Object proxy, Method method, Object[] args);
    }

}
